package cn.iocoder.yudao.module.digital.service.cardkeymanager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.currency.api.rate.ExchangeRateApi;
import cn.iocoder.yudao.module.digital.api.cardkeymanager.dto.CardKeySecretDTO;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeysecret.vo.CardkeySecretSaveReqVO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyproduct.CardkeyProductDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeysecret.CardkeySecretDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeythirdpartyapirecord.CardkeyThirdPartyApiRecordDO;
import cn.iocoder.yudao.module.digital.dal.redis.cardkeymanager.CardKeyManagerLockRedisDAO;
import cn.iocoder.yudao.module.digital.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.CardKeyApiRequestTypeEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.CardKeySourceEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.ClaimStatusEnum;
import cn.iocoder.yudao.module.digital.framework.cardkey.config.CardKeyConfig;
import cn.iocoder.yudao.module.digital.service.cardchannel.model.*;
import cn.iocoder.yudao.module.digital.service.cardkeyproduct.CardkeyProductService;
import cn.iocoder.yudao.module.digital.service.cardkeysecret.CardkeySecretService;
import cn.iocoder.yudao.module.digital.service.cardchannel.impl.CardChannelManagerImpl;
import cn.iocoder.yudao.module.digital.service.cardchannel.impl.bh.ICardKeyThirdPartyBHApi;
import cn.iocoder.yudao.module.digital.service.cardchannel.ICardChannel;
import cn.iocoder.yudao.module.digital.service.cardchannel.constant.CardKeyThirdPartyApiConstant;
import cn.iocoder.yudao.module.digital.service.cardchannel.model.common.CdkProduct;
import cn.iocoder.yudao.module.digital.service.cardkeythirdpartyapirecord.CardkeyThirdPartyApiRecordService;
import cn.iocoder.yudao.module.infra.util.TraceIdUtil;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderDeliveryStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static net.sf.jsqlparser.util.validation.metadata.NamedObject.index;


/**
 * Description:
 * Author: li
 * Date: 2024/3/5
 */
@Slf4j
@Service
@Deprecated
public class CardKeyManagerServiceImpl implements ICardKeyManagerService {
    private final ExecutorService generateSecretExecutor = ThreadUtil.newFixedExecutor(32, "generate-secret", true);
    private final String productNoManualDelivery = "ManualDelivery"; // 手动发货商品编号
    private static final long ORDER_TIMEOUT_MILLIS = 60 * DateUtils.SECOND_MILLIS * 15;
    private static final long PRODUCT_TIMEOUT_MILLIS = 60 * DateUtils.SECOND_MILLIS * 15;
    private static final long GEN_SECRET_TIMEOUT_MILLIS = 60 * DateUtils.SECOND_MILLIS * 10;

    @Resource
    private ProductSkuApi skuApi;
    @Resource
    private TradeOrderApi orderApiProxy;
    @Resource
    private CardkeyProductService cardkeyProductService;
    @Resource
    private CardkeySecretService cardkeySecretService;
    @Resource
    private ICardKeyThirdPartyBHApi cardKeyThirdPartyApi;
    @Resource
    private CardKeyManagerLockRedisDAO lockRedisDAO;
    @Resource
    private CardKeyConfig cardKeyConfig;
    @Resource
    private ExchangeRateApi exchangeRateApi;
    @Resource
    private CardkeyThirdPartyApiRecordService apiRecordService;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private CardChannelManagerImpl thirdPartyServiceManager;

    /**
     * 通过客服领取卡密
     *
     * @param orderNo 订单流水号
     * @param secret  密钥
     */
    @Override
    public List<CardKeySecretDTO> claimCardKeySecretByCustomerService(String orderNo, String secret) {
        log.info("通过客服领取卡密-开始, orderNo = {}, secret = {}", orderNo, secret);
        final long orderTimeoutMs = 60 * DateUtils.SECOND_MILLIS;
        final List<CardKeySecretDTO> cardKeySecretList = lockRedisDAO.lockOrder(orderNo, orderTimeoutMs, () -> {
            log.info("通过客服领取卡密-获取到订单锁, orderNo = {}", orderNo);

            // 查询订单发货状态
            final TradeOrderRespDTO order = orderApiProxy.getOrderByNo(orderNo);
            log.info("通过客服领取卡密-查询订单发货状态, orderNo = {}, deliveryStatus = {}", orderNo, order.getDeliveryStatus());

            if (!order.getPayStatus()) {
                throw new IllegalStateException(String.format("订单未支付 [%s]", orderNo));
            }

            if (TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus())) {
                log.warn("通过客服领取卡密-订单已发货, orderNo = {}", orderNo);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, "订单已发货，请勿重复发货");
            }

            // 存储人工填写的密钥 & 更新订单发货状态
            final List<CardKeySecretDTO> secretDTOList = createCardKeySecretByManualAndUpdateOrderStatus(orderNo, secret, order);
            return secretDTOList;
        });
        return cardKeySecretList;
    }

    // 存储人工填写的密钥 & 更新订单发货状态
    @Transactional(rollbackFor = Throwable.class)
    public List<CardKeySecretDTO> createCardKeySecretByManualAndUpdateOrderStatus(String orderNo, String secret, TradeOrderRespDTO order) {
        // 存储人工填写的密钥
        final Long userId = order.getUserId();
        final Integer denomination = order.getPaymentPrice();
        final String orderCurrency = order.getOrderCurrency();
        final List<CardKeySecretDTO> secretDTOList = this.createCardKeySecretByManual(denomination, orderCurrency, userId, orderNo, secret);
        log.info("通过客服领取卡密-成功, orderNo = {}, secretDTOList = {}", orderNo, secretDTOList);

        // 更新订单发货状态
        this.updateOrderDeliveryStatus(orderNo, true, null);

        return secretDTOList;
    }

    // 存储人工填写的密钥
    private List<CardKeySecretDTO> createCardKeySecretByManual(Integer denomination, String currencyCode, Long userId, String orderNo, String secret) {
        final List<CardKeySecretDTO> secretDTOList = parseSecretData(secret);
        for (CardKeySecretDTO secretDTO : secretDTOList) {
            final CardkeySecretDO existedSecret = cardkeySecretService.getCardKeySecretByNumberCodePin(
                    ClaimStatusEnum.YES, secretDTO.getCardNumber(), secretDTO.getCode(), secretDTO.getPin());
            if (existedSecret != null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR,
                        String.format("该卡密已经被使用, cardNumber = %s, code = %s, pin = %s",
                                secretDTO.getCardNumber(), secretDTO.getCode(), secretDTO.getPin()));
            }

            final CardkeySecretSaveReqVO vo = new CardkeySecretSaveReqVO();
            vo.setSource(CardKeySourceEnum.MANUAL.getValue());
            vo.setProductNo(productNoManualDelivery);
            vo.setClaimStatus(ClaimStatusEnum.YES.getValue());
            vo.setClaimDate(LocalDateTime.now());
            vo.setDenomination(denomination);
            vo.setCurrencyCode(currencyCode);
            final String requestId = IdUtil.fastSimpleUUID();
            vo.setRequestId(requestId);
            vo.setOrderNo(orderNo);
            vo.setUserId(userId);
            vo.setCardNumber(secretDTO.getCardNumber());
            vo.setCode(secretDTO.getCode());
            vo.setPin(secretDTO.getPin());
            cardkeySecretService.createCardkeySecret(vo);
        }
        return secretDTOList;
    }

    @Override
    public List<CardKeySecretDTO> parseSecretData(String textData) {
        String[] lines = textData.trim().split("\\n");

        List<CardKeySecretDTO> cardKeySecrets = new ArrayList<>();

        // Iterate through each line and parse the data into CardKeySecretDTO objects
        for (String line : lines) {
            String[] parts = line.split("->");
            if (parts.length == 3) {
                CardKeySecretDTO cardKeySecret = new CardKeySecretDTO(getSecretFieldValue(parts[0]), getSecretFieldValue(parts[1]), getSecretFieldValue(parts[2]));
                cardKeySecrets.add(cardKeySecret);
            } else {
                throw new RuntimeException("Invalid data format: " + line);
            }
        }

        return cardKeySecrets;
    }

    private String getSecretFieldValue(String value) {
        if ("null".equalsIgnoreCase(value)) {
            return null;
        }
        return value;
    }

    /*************************************领取卡密**************************************/

    /**
     * 领取卡密
     *
     * @param orderNo         订单流水号
     * @param skuId           sku的id
     * @param skuNum          sku的数量
     * @param buyDenomination 用户输入的面额（单位：分）
     * @return
     */
    @Override
    public List<CardKeySecretDTO> claimCardKeySecret(String orderNo, Long skuId, int skuNum, Integer buyDenomination) {
        try {
            TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());
            log.info("领取卡密-开始, orderNo = {} skuId = {}, skuNum = {}, buyDenomination = {}",
                    orderNo, skuId, skuNum, buyDenomination);

            final List<CardkeySecretDO> secretDOList = lockRedisDAO.lockOrder(orderNo, ORDER_TIMEOUT_MILLIS, () -> {
                log.info("领取卡密-获取到订单锁, orderNo = {}", orderNo);

                // 查询订单发货状态
                final TradeOrderRespDTO order = orderApiProxy.getOrderByNo(orderNo);
                log.info("领取卡密-查询订单发货状态1, orderNo = {}, deliveryStatus = {}", orderNo, order.getDeliveryStatus());

                if (!order.getPayStatus()) {
                    throw new IllegalStateException(String.format("订单未支付 [%s]", orderNo));
                }

                if (TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus())) {
                    // 查询已发货的卡密
                    final List<CardkeySecretDO> secretList = cardkeySecretService.getCardKeySecretByOrderNo(orderNo);
                    final List<String> secretIdList = secretList.stream().map(v -> v.getId()).collect(Collectors.toList());
                    log.warn("领取卡密-订单已发货1, orderNo = {}, secretIdList = {}", orderNo, secretIdList);
                    return secretList;
                }

                // 领取卡密
                return claimCardKeySecret0(order, orderNo, skuId, skuNum, buyDenomination);
            });

            final List<CardKeySecretDTO> secretList = secretDOList.stream().map(v -> new CardKeySecretDTO(v.getCardNumber(), v.getCode(), v.getPin())).collect(Collectors.toList());
            return secretList;
        } finally {
            TraceIdUtil.removeTraceId();
        }
    }

    private List<CardkeySecretDO> claimCardKeySecret0(TradeOrderRespDTO order, String orderNo, Long skuId, int skuNum, Integer buyDenomination) {
        final Long userId = order.getUserId();
        try {
            // 查询sku
            final ProductSkuRespDTO sku = skuApi.getSku(skuId);
            final String cardKeyProductNo = sku.getCardKeyProductNo();

            // 查询卡密商品
            final CardkeyProductDO cardKeyProduct = cardkeyProductService.getCardKeyProductByNo(cardKeyProductNo);
            log.info("领取卡密-查询卡密商品, cardKeyProductNo = {}, cardKeyProduct = {}", cardKeyProductNo, cardKeyProduct);

            // 获取面额
            final Integer denomination = getDenomination(userId, orderNo, buyDenomination, cardKeyProduct, sku);

            // 检查订单金额是否大于商品面值，防止上有业务有bug导致亏损
            if (cardKeyConfig.getVerifyPaymentPrice()) {
                final BigDecimal paymentPriceYuan = new BigDecimal(order.getPaymentPrice()).divide(new BigDecimal(100));
                // 将面额换算成结算货币
                final String currencyCode = cardKeyProduct.getCurrencyCode();
                final Integer denominationPriceFen = exchangeRateApi.calculateWithCurrency(denomination * skuNum, currencyCode);
                final BigDecimal denominationPriceYuan = new BigDecimal(denominationPriceFen).divide(new BigDecimal(100));
                if (paymentPriceYuan.compareTo(denominationPriceYuan) < 0) {
                    log.error("领取卡密-失败-订单金额不能小于商品面值, cardKeyProductNo = {}, paymentPriceYuan = {}, denominationPriceYuan = {}",
                            cardKeyProductNo, paymentPriceYuan, denominationPriceYuan);
                    throw new RuntimeException(String.format("订单金额不能小于商品面值 [%s元 < %s元]", paymentPriceYuan, denominationPriceYuan));
                }
            }

            log.info("领取卡密-获取卡密商品锁-开始, userId = {}, orderNo = {}, cardKeyProductNo = {}", userId, orderNo, cardKeyProductNo);
            final CardKeySourceEnum source = CardKeySourceEnum.fromValue(cardKeyProduct.getSource());
            final List<CardkeySecretDO> secretList = lockRedisDAO.lockProduct(source, cardKeyProductNo, denomination, PRODUCT_TIMEOUT_MILLIS, () -> {
                log.info("领取卡密-获取卡密商品锁-成功, userId = {}, orderNo = {}, cardKeyProductNo = {}", userId, orderNo, cardKeyProductNo);
                return claimCardKeySecret1(userId, orderNo, skuNum, cardKeyProductNo, denomination, cardKeyProduct);
            });
            return secretList;
        } catch (Exception e) {
            log.error(String.format("领取卡密-异常, userId = %s, orderNo = %s", userId, orderNo), e);
            updateOrderDeliveryStatus(orderNo, false, e.getMessage());
            throw e;
        }
    }

    // 获取面额
    private static Integer getDenomination(Long userId, String orderNo, Integer buyDenomination,
                                           CardkeyProductDO cardKeyProduct, ProductSkuRespDTO sku) {
        final Integer denomination;
        if (cardKeyProduct.getCustomDenomination()) {
            // product自定义面额
            if (sku.getCustomDenomination()) {
                // sku自定义面额，使用用户传入的
                denomination = buyDenomination;
            } else {
                // sku固定面额
                denomination = sku.getDenominationFixed();
            }
        } else {
            // product固定面额，使用商品中定义好的
            denomination = cardKeyProduct.getDenominationFixed();
        }
        if (denomination == null) {
            log.info("领取卡密-失败-面额为空, userId = {}, orderNo = {}, productNo = {}, customDenomination = {}, productDenomination = {}, buyDenomination = {}",
                    userId, orderNo, cardKeyProduct.getProductNo(), cardKeyProduct.getCustomDenomination(), cardKeyProduct.getDenominationFixed(), buyDenomination);
            throw new RuntimeException(String.format("领取卡密失败-面额为空, customDenomination = %s, productDenomination = %s, buyDenomination = %s",
                    cardKeyProduct.getCustomDenomination(), cardKeyProduct.getDenominationFixed(), buyDenomination));
        }

        log.info("领取卡密-获取面额, cardKeyProductNo = {}, denomination = {}", cardKeyProduct.getProductNo(), denomination);
        return denomination;
    }

    public List<CardkeySecretDO> claimCardKeySecret1(Long userId, String orderNo, int skuNum, String productNo,
                                                     Integer denomination, CardkeyProductDO cardKeyProduct) {
        // 查询当前可用的密钥
        final List<CardkeySecretDO> availableSecretList1 = cardkeySecretService.getCardKeySecretByProductNoAndClaimStatus(
                ClaimStatusEnum.NO, productNo, denomination, skuNum);
        final int missingSecretSize1 = skuNum - availableSecretList1.size();
        log.info("领取卡密-查询当前可用的密钥1, productNo = {}, denomination = {}, availableSecretList1.size = {}, skuNum = {}, missingSecretSize1 = {}",
                productNo, denomination, availableSecretList1.size(), skuNum, missingSecretSize1);

        if (missingSecretSize1 == 0) {
            // 扣减密钥，并更新订单发货状态
            safeDeductionSecretAndUpdateOrderStatus(userId, orderNo, availableSecretList1);
            return availableSecretList1;
        }

        // 判断当前卡密商品来源
        final CardKeySourceEnum cardKeySourceEnum = CardKeySourceEnum.fromValue(cardKeyProduct.getSource());
        if (cardKeySourceEnum == CardKeySourceEnum.MANUAL) {
            // 手动导入，库存不够只能失败
            // 更新订单发货状态为失败
            updateOrderDeliveryStatus(orderNo, false, String.format("采购的卡密库存不足，需要数量%s，当前可用数量%s",
                    skuNum, availableSecretList1.size()));
            log.info("领取卡密-失败-采购的卡密库存不足, userId = {}, orderNo = {}, productNo = {}, source = {}, denomination = {}, skuNum = {}, missingSecretSize1 = {}",
                    userId, orderNo, productNo, cardKeyProduct.getSource(), denomination, skuNum, missingSecretSize1);
            throw new RuntimeException(String.format("采购的卡密库存不足，需要数量%s，当前可用数量%s", skuNum, availableSecretList1.size()));
        } else {
            // 调用api生成密钥
            final List<String> errorMsgList = generateSecret(userId, orderNo, productNo, missingSecretSize1, denomination, cardKeyProduct);

            // 再次发货
            return retryDelivery(userId, orderNo, skuNum, productNo, denomination, errorMsgList);
        }
    }

    private List<CardkeySecretDO> retryDelivery(Long userId, String orderNo, int skuNum, String productNo, Integer denomination, List<String> errorMsgList) {
        // 重新查询当前可用的密钥
        final List<CardkeySecretDO> availableSecretList2 = cardkeySecretService.getCardKeySecretByProductNoAndClaimStatus(
                ClaimStatusEnum.NO, productNo, denomination, skuNum);
        final int missingSecretSize2 = skuNum - availableSecretList2.size();
        log.info("领取卡密-查询当前可用的密钥2, productNo = {}, denomination = {}, availableSecretList2.size = {}, skuNum = {}, missingSecretSize2 = {}",
                productNo, denomination, availableSecretList2.size(), skuNum, missingSecretSize2);

        if (missingSecretSize2 == 0) {
            // 库存足够
            // 查询订单发货状态
            final TradeOrderRespDTO order = orderApiProxy.getOrderByNo(orderNo);
            log.info("领取卡密-查询订单发货状态2, userId = {}, orderNo = {}, deliveryStatus = {}",
                    userId, orderNo, order.getDeliveryStatus());
            if (TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus())) {
                final List<CardkeySecretDO> secretList = cardkeySecretService.getCardKeySecretByOrderNo(orderNo);
                final List<String> secretIdList = secretList.stream().map(v -> v.getId()).collect(Collectors.toList());
                log.warn("领取卡密-订单已发货2, userId = {}, orderNo = {}, secretIdList = {}", userId, orderNo, secretIdList);
                return secretList;
            } else {
                // 扣减密钥，并更新订单发货状态
                safeDeductionSecretAndUpdateOrderStatus(userId, orderNo, availableSecretList2);
                return availableSecretList2;
            }
        } else {
            // 库存不足
            // 更新订单发货状态为失败
            final StringBuilder errorMsgBuilder = new StringBuilder(String.format("黑鹰生成卡密失败，需要数量%s，当前可用数量%s",
                    skuNum, availableSecretList2.size()));
            if (CollUtil.isNotEmpty(errorMsgList)) {
                for (String str : errorMsgList) {
                    errorMsgBuilder.append("/n").append(str);
                }
            }
            final String errorMsg = errorMsgBuilder.toString();
            updateOrderDeliveryStatus(orderNo, false, errorMsg);
            log.error("领取卡密-失败-黑鹰生成卡密失败, userId = {}, orderNo = {}, productNo = {}, denomination = {}, skuNum = {}, missingSecretSize2 = {}, errorMsg = {}",
                    userId, orderNo, productNo, denomination, skuNum, missingSecretSize2, errorMsg);
            throw new RuntimeException(errorMsg);
        }
    }

    // 扣减密钥，并更新订单发货状态
    private void safeDeductionSecretAndUpdateOrderStatus(Long userId, String orderNo, List<CardkeySecretDO> availableSecretList) {
        try {
            deductionSecretAndUpdateOrderStatus(userId, orderNo, availableSecretList);
        } catch (Exception e) {
            final Set<String> secretIds = availableSecretList.stream().map(v -> v.getId()).collect(Collectors.toSet());
            log.error(String.format("领取卡密-异常, userId = %s, orderNo = %s, secretIds = %s", userId, orderNo, secretIds), e);

            // 更新订单发货状态为失败
            updateOrderDeliveryStatus(orderNo, false, e.getMessage());
            throw e;
        }
    }

    // 扣减密钥，并更新订单发货状态
    @Transactional(rollbackFor = Throwable.class)
    public void deductionSecretAndUpdateOrderStatus(Long userId, String orderNo, List<CardkeySecretDO> availableSecretList) {
        // 扣减密钥
        this.deductionSecret(userId, orderNo, availableSecretList);

        // 更新订单发货状态
        this.updateOrderDeliveryStatus(orderNo, true, null);
    }

    // 更新订单发货状态
    private void updateOrderDeliveryStatus(String orderNo, boolean success, String reason) {
        if (reason != null && reason.startsWith("java.lang.RuntimeException: ")) {
            reason = reason.replace("java.lang.RuntimeException: ", "");
        }
        if (success) {
            log.info("领取卡密-更新订单发货状态为成功, orderNo = {}, reason = {}", orderNo, reason);
        } else {
            log.info("领取卡密-更新订单发货状态为失败, orderNo = {}, reason = {}", orderNo, reason);
        }
        orderApiProxy.deliveryOrderByNo(orderNo, null, null, success, reason);
    }

    // 扣减密钥
    private void deductionSecret(Long userId, String orderNo, List<CardkeySecretDO> availableSecretList) {
        final Set<String> secretIds = availableSecretList.stream().map(v -> v.getId()).collect(Collectors.toSet());
        log.info("领取卡密-扣减密钥-开始, userId = {}, orderNo = {}, secretIds = {}", userId, orderNo, secretIds);
        cardkeySecretService.deductionSecret(userId, orderNo, secretIds);

        log.info("领取卡密-扣减密钥-成功, userId = {}, orderNo = {}, secretIds = {}", userId, orderNo, secretIds);
    }

    // 调用api生成密钥
    private List<String> generateSecret(Long userId, String orderNo, String productNo, int missingSecretSize,
                                        Integer denomination, CardkeyProductDO cardKeyProduct) {
        log.info("领取卡密-调用api生成密钥-开始, productNo = {}, denomination = {}, missingSecretSize = {}",
                productNo, denomination, missingSecretSize);

        final CountDownLatch latch = new CountDownLatch(missingSecretSize);
        final AtomicInteger successSize = new AtomicInteger();
        final List<String> errorMsgList = Collections.synchronizedList(new ArrayList<>());

        final String traceId = TraceIdUtil.getTraceId();
        for (int i = 0; i < missingSecretSize; i++) {
            int index = i;
            generateSecretExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        TraceIdUtil.setTraceId(traceId);

                        final GenerateEGiftResponseWrapper responseWrapper = generateSecret(userId, orderNo, productNo, denomination, cardKeyProduct, index, missingSecretSize);
                        if (!responseWrapper.isGenerateEGiftSuccess()) {
                            // 生成卡密失败
                            final String errorMsg = responseWrapper.getGenerateEGiftErrorMsg();
                            errorMsgList.add(errorMsg);
                        } else {
                            final GetAccountResponseWrapper getAccountResponseWrapper = responseWrapper.getGetAccountResponseWrapper();
                            if (!getAccountResponseWrapper.isSuccess()) {
                                // 查询卡密账户失败
                                final String errorMsg = getAccountResponseWrapper.getErrorMsg();
                                errorMsgList.add(errorMsg);
                            }
                        }

                        successSize.incrementAndGet();
                    } finally {
                        latch.countDown();
                        TraceIdUtil.removeTraceId();
                    }
                }
            });
        }

        try {
            latch.await(GEN_SECRET_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error(String.format("领取卡密-调用api生成密钥-超时, productNo = %s, denomination = %s, successSize = %s, missingSecretSize = %s",
                    productNo, denomination, successSize.get(), missingSecretSize), e);
            throw new RuntimeException("调用api生成密钥-超时", e);
        }

        log.info("领取卡密-调用api生成密钥-完成, productNo = {}, denomination = {}, successSize = {}, missingSecretSize = {}",
                productNo, denomination, successSize.get(), missingSecretSize);
        return errorMsgList;
    }

    // 调用api生成密钥
    private GenerateEGiftResponseWrapper generateSecret(Long userId, String orderNo, String productNo, Integer denomination,
                                                        CardkeyProductDO cardKeyProduct, int index, int total) {
        log.info("领取卡密-调用api生成密钥-单个开始, productNo = {}, denomination = {}, index = {}, total = {}",
                productNo, denomination, index, total);

        final String currencyCode = cardKeyProduct.getCurrencyCode();
        final GenerateEGiftResponseWrapper generateCardResult;
        try {
            final GenerateEGiftRequest request = createGenerateEGiftRequestDTO(orderNo, denomination, cardKeyProduct);
            final GenerateEGiftRequestWrapper requestWrapper = new GenerateEGiftRequestWrapper(request, orderNo, productNo, denomination, currencyCode);
            generateCardResult = cardKeyThirdPartyApi.generateCard(requestWrapper);
        } catch (Exception e) {
            log.error(String.format("领取卡密-调用api生成密钥-单个失败, productNo = %s, denomination = %s, index = %s, total = %s",
                    productNo, denomination, index, total), e);
            throw e;
        }

        if (generateCardResult.isGenerateEGiftSuccess()
                && generateCardResult.getGetAccountResponseWrapper() != null
                && generateCardResult.getGetAccountResponseWrapper().isSuccess()) {

            log.info("领取卡密-调用api生成密钥-单个成功, productNo = {}, denomination = {}, index = {}, total = {}, generateCardResult = {}",
                    productNo, denomination, index, total, generateCardResult);

            // 存储api生成的密钥 & 更新查询卡密账户请求成功
            final String generateEGiftRequestId = generateCardResult.getGenerateEGiftRequestId();
            final String secretId = saveSecretFromAPIWithUpdateGetAccountSuccess(productNo, denomination, generateEGiftRequestId,
                    currencyCode, generateCardResult.getGenerateEGiftResponse(), generateCardResult.getGetAccountResponseWrapper());
            log.info("领取卡密-存储api生成的密钥, productNo = {}, denomination = {}, index = {}, secretId = {}", productNo, denomination, index, secretId);
        } else {
            log.error("领取卡密-调用api生成密钥-单个失败, productNo = {}, denomination = {}, index = {}, total = {}, generateCardResult = {}",
                    productNo, denomination, index, total, generateCardResult);
        }

        return generateCardResult;
    }

    // 存储api生成的密钥 & 更新查询卡密账户请求成功
    private String saveSecretFromAPIWithUpdateGetAccountSuccess(String productNo, Integer denomination,
                                                              String generateEGiftRequestId, String currencyCode,
                                                              GenerateEGiftResponse generateEGiftResponse,
                                                              GetAccountResponseWrapper getAccountResponseWrapper) {
        final GetAccountResponse getAccountResponse = getAccountResponseWrapper.getResponse();

        // 存储api生成的密钥
        final String entityId = generateEGiftResponse.getEntityId();
        final String eGiftId = entityId.substring(entityId.lastIndexOf("/") + 1);

        final String cardAccountIdUrl = generateEGiftResponse.getAccountId();
        final String cardAccountId = parseAccountId(cardAccountIdUrl);

        final String retrievalReferenceNumber = generateEGiftResponse.getRetrievalReferenceNumber();
        final CardkeySecretSaveReqVO vo = new CardkeySecretSaveReqVO();
        vo.setSource(CardKeySourceEnum.BLACK_HAWK_API.getValue());
        vo.setProductNo(productNo);
        vo.setClaimStatus(ClaimStatusEnum.NO.getValue());
        vo.setCurrencyCode(currencyCode);
        vo.setEGiftId(eGiftId);
        vo.setDenomination(denomination);
        vo.setCardNumber(getAccountResponse.getActivationAccountNumber()); // (Card) Serial Number
        vo.setCode(getAccountResponse.getAccountNumber()); // Redeem Code
        vo.setPin(getAccountResponse.getSecurityCode()); // Security Pin of an Account
        vo.setRequestId(generateEGiftRequestId);
        vo.setCardAccountId(cardAccountId);
        vo.setRetrievalReferenceNumber(retrievalReferenceNumber);

        // 因为在线程池中执行没有被Bean管理，只能手动提交事务
        final String secretId = transactionTemplate.execute(new TransactionCallback<String>() {
            @Override
            public String doInTransaction(TransactionStatus status) {
                final String secretId = cardkeySecretService.createCardkeySecret(vo);

                // 更新查询卡密账户请求成功
                apiRecordService.updateApiRecordWithSuccessResponse(getAccountResponseWrapper.getApiRecordId(), JsonUtils.toJsonString(getAccountResponse));

                return secretId;
            }
        });

        return secretId;
    }

    private String parseAccountId(String urlString) {
        // 找到accountId参数在URL中的起始位置
        int accountIdIndex = urlString.indexOf("accountId=");

        // 如果找到了accountId参数
        if (accountIdIndex != -1) {
            // 截取accountId参数值
            final String accountId = urlString.substring(accountIdIndex + "accountId=".length());
            return accountId;
        }
        return null;
    }

    private GenerateEGiftRequest createGenerateEGiftRequestDTO(String orderNo, Integer denomination, CardkeyProductDO cardKeyProduct) {
        final GenerateEGiftRequest generateCardRequest = new GenerateEGiftRequest();
        final String giftAmount = new BigDecimal(denomination).divide(new BigDecimal(100)).toPlainString(); // 分转换为元
        generateCardRequest.setGiftAmount(giftAmount);
        generateCardRequest.setProductConfigurationId(cardKeyProduct.getConfigurationId());
//        final GenerateEGiftRequest.User purchaserInfo = new GenerateEGiftRequest.User();
//        final int countryIndex = cardKeyConfig.getCountries().size() > 1 ? RandomUtil.randomInt(0, cardKeyConfig.getCountries().size() - 1) : 0;
//        final CardKeyConfig.Country country = cardKeyConfig.getCountries().get(countryIndex);
//        final int stateCodeIndex = country.getStateCodes().size() > 1 ? RandomUtil.randomInt(0, country.getStateCodes().size() - 1) : 0;
//        final String stateCode = country.getStateCodes().get(stateCodeIndex);
//        purchaserInfo.setState(stateCode);
//        purchaserInfo.setCountryCode(country.getCountryCode());
//        generateCardRequest.setPurchaserInfo(purchaserInfo);

        generateCardRequest.setEGiftDetails(new GenerateEGiftRequest.EGiftDetails());
        generateCardRequest.getEGiftDetails().setAdditionalAttributes(Arrays.asList(new GenerateEGiftRequest.AdditionalAttribute("UMS.62.5", "String", orderNo)));

        return generateCardRequest;
    }

    /*************************************重试失败的请求**************************************/


    // 撤销失败的卡密
    @Override
    public String revertGenFailedRequest() {
        final String traceId = IdUtil.fastSimpleUUID();
        log.info("撤销失败的卡密-开始, traceId = {}", traceId);

        int pageNum = 0;
        int successSize = 0;
        int failedSize = 0;
        int totalSize = 0;
        while (true) {
            // 查询生成卡失败 > minute的数据
            pageNum++;
            final ListByHttpStatusCodesVO vo = new ListByHttpStatusCodesVO();
            vo.setRequestType(CardKeyApiRequestTypeEnum.GENERATE);
            vo.setHttpStatusCodes(CardKeyThirdPartyApiConstant.HTTP_STATUS_CODE_REVERSE2);
            vo.setMinutesAgo(CardKeyThirdPartyApiConstant.RETRY_TASK_MINUTES_AGO);
            vo.setPageSize(CardKeyThirdPartyApiConstant.RETRY_API_RECORD_LIMIT_SIZE);
            vo.setPageNo(pageNum);
            final PageResult<CardkeyThirdPartyApiRecordDO> pageResult = apiRecordService.listByHttpStatusCodesNotRevert(vo);

            final Long total = pageResult.getTotal();
            final List<CardkeyThirdPartyApiRecordDO> apiRecordList = pageResult.getList();
            totalSize += apiRecordList.size();
            log.info("撤销失败的卡密-分页查询, traceId = {}, pageNum = {}, total = {}, apiRecordList.size = {}",
                    traceId, pageNum, total, apiRecordList);
            if (CollUtil.isEmpty(apiRecordList)) {
                break;
            }

            for (CardkeyThirdPartyApiRecordDO recordDO : apiRecordList) {
                try {
                    // 撤销卡密
                    TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());

                    final String generateEGiftRequestId = recordDO.getRequestId();
                    final String orderNo = recordDO.getOrderNo();
                    log.info("撤销失败的卡密-撤销卡密-开始, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}",
                            traceId, recordDO.getId(), orderNo, generateEGiftRequestId);

                    final GenerateEGiftRequestWrapper requestWrapper = new GenerateEGiftRequestWrapper(null, orderNo, recordDO.getProductNo(), recordDO.getDenomination(), recordDO.getCurrencyCode());
                    final ReverseEGiftResponse response = cardKeyThirdPartyApi.callReverseCardAPI(0, generateEGiftRequestId, requestWrapper);
                    if (response.isSuccess()) {
                        // 撤销成功
                        apiRecordService.updateApiRecordToReverseSuccess(recordDO.getId());
                        successSize++;
                        log.info("撤销失败的卡密-撤销卡密-成功, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}",
                                traceId, recordDO.getId(), orderNo, generateEGiftRequestId);
                    } else {
                        failedSize++;
                        final String errorMsg = response.getErrorMsg();
                        log.error("撤销失败的卡密-撤销卡密-失败, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}, errorMsg = {}",
                                traceId, recordDO.getId(), orderNo, generateEGiftRequestId, errorMsg);
                    }
                } finally {
                    TraceIdUtil.removeTraceId();
                }
            }
        }

        log.info("撤销失败的卡密-结束, traceId = {}, totalSize = {}, successSize = {}, failedSize = {}",
                traceId, totalSize, successSize, failedSize);
        return String.format("撤销失败的卡密, traceId = %s, 总数量 = %s, 成功数量 = %s, 失败数量 = %s",
                traceId, totalSize, successSize, failedSize);
    }

    // 重试查询卡密账户失败
    @Override
    public String retryGetAccountFailedRequest() {
        final String traceId = IdUtil.fastSimpleUUID();
        log.info("重试查询卡密账户失败的记录-开始, traceId = {}", traceId);

        int pageNum = 0;
        int successSize = 0;
        int failedSize = 0;
        int totalSize = 0;
        while (true) {
            // 查询查询卡密账户失败 > minute的数据
            pageNum++;
            final ListByFailedHttpCodeVO vo = new ListByFailedHttpCodeVO();
            vo.setRequestType(CardKeyApiRequestTypeEnum.GET_ACCOUNT);
            vo.setMinutesAgo(CardKeyThirdPartyApiConstant.RETRY_TASK_MINUTES_AGO);
            vo.setPageSize(CardKeyThirdPartyApiConstant.RETRY_API_RECORD_LIMIT_SIZE);
            vo.setPageNo(pageNum);
            final PageResult<CardkeyThirdPartyApiRecordDO> pageResult = apiRecordService.listByFailedHttpCodeNotRevert(vo);

            final Long total = pageResult.getTotal();
            final List<CardkeyThirdPartyApiRecordDO> apiRecordList = pageResult.getList();
            totalSize += apiRecordList.size();
            log.info("重试查询卡密账户失败的记录-分页查询, traceId = {}, pageNum = {}, total = {}, apiRecordList.size = {}",
                    traceId, pageNum, total, apiRecordList);
            if (CollUtil.isEmpty(apiRecordList)) {
                break;
            }

            for (CardkeyThirdPartyApiRecordDO recordDO : apiRecordList) {
                try {
                    // 查询账户
                    TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());
                    final Long apiRecordId = recordDO.getId();

                    // 查询生成卡密的记录
                    final String generateEGiftRequestId = recordDO.getGenerateEGiftRequestId();
                    final String orderNo = recordDO.getOrderNo();
                    final CardkeyThirdPartyApiRecordDO genCardRecord = apiRecordService.getCardkeyThirdPartyApiRecordByRequestIdAndType(CardKeyApiRequestTypeEnum.GENERATE, generateEGiftRequestId);
                    if (genCardRecord.getHttpStatusCode() != CardKeyThirdPartyApiConstant.HTTP_STATUS_CODE_SUCCESS) {
                        log.error("重试查询卡密账户失败的记录-查询生成卡密的记录-状态非成功, traceId = {}, orderNo = {}, generateEGiftRequestId = {}, httpStatusCode = {}",
                                traceId, orderNo, generateEGiftRequestId, genCardRecord.getHttpStatusCode());
                        continue;
                    }
                    final GenerateEGiftResponse generateEGiftResponse = JsonUtils.parseObject(genCardRecord.getResponseBody(), GenerateEGiftResponse.class);

                    log.info("重试查询卡密账户失败的记录-查询账户-开始, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}",
                            traceId, apiRecordId, orderNo, generateEGiftRequestId);
                    final String requestUrl = recordDO.getRequestUrl();
                    final String requestHeaders = recordDO.getRequestHeaders();
                    final Map<String, String> headers = JsonUtils.parseObject(requestHeaders, Map.class);
                    final GetAccountResponseWrapper getAccountResult = cardKeyThirdPartyApi.callGetAccountAPI(0, apiRecordId, requestUrl, headers);
                    if (getAccountResult.isSuccess()) {
                        // 成功
                        log.info("重试查询卡密账户失败的记录-查询账户-成功, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}",
                                traceId, recordDO.getId(), orderNo, generateEGiftRequestId);

                        // 存储api生成的密钥 & 更新查询卡密账户请求成功
                        final String productNo = recordDO.getProductNo();
                        final Integer denomination = recordDO.getDenomination();
                        final String currencyCode = recordDO.getCurrencyCode();
                        final String secretId = saveSecretFromAPIWithUpdateGetAccountSuccess(productNo, denomination, generateEGiftRequestId, currencyCode, generateEGiftResponse, getAccountResult);

                        successSize++;
                        log.info("领取卡密-存储api生成的密钥, traceId = {}, productNo = {}, denomination = {}, index = {}, secretId = {}",
                                traceId, productNo, denomination, index, secretId);
                    } else {
                        failedSize++;
                        final String errorMsg = getAccountResult.getErrorMsg();
                        log.error("重试查询卡密账户失败的记录-查询账户-失败, traceId = {}, apiRecordId = {}, orderNo = {}, generateEGiftRequestId = {}, errorMsg = {}",
                                traceId, recordDO.getId(), orderNo, generateEGiftRequestId, errorMsg);
                    }
                } finally {
                    TraceIdUtil.removeTraceId();
                }
            }
        }

        log.info("重试查询卡密账户失败的记录-结束, traceId = {}, totalSize = {}, successSize = {}, failedSize = {}",
                traceId, totalSize, successSize, failedSize);
        return String.format("重试查询卡密账户失败的记录, traceId = %s, 总数量 = %s, 成功数量 = %s, 失败数量 = %s",
                traceId, totalSize, successSize, failedSize);
    }


    /*************************************从api拉取卡密商品**************************************/

    /**
     * 从api拉取卡密商品
     */
    @Override
    public void synCardKeyProductFromApi() {
        final Boolean enableSynCardKeyProduct = cardKeyConfig.getEnableSynCardKeyProduct();
        log.info("获取卡密商品-开始, enableSynCardKeyProduct = {}", enableSynCardKeyProduct);
        if (!enableSynCardKeyProduct) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, "未打开同步卡密商品");
        }

        final List<CardKeySourceEnum> sourceEnumList = Arrays.asList(CardKeySourceEnum.BLACK_HAWK_API, CardKeySourceEnum.BAMBOO_API);
        for (CardKeySourceEnum sourceEnum : sourceEnumList) {
            final ICardChannel impl = thirdPartyServiceManager.getChannelImpl(sourceEnum);
            final List<CdkProduct> products = impl.getAllProducts();
            for (CdkProduct product : products) {
                final String productNo = product.getProductNo();
                CardkeyProductDO productDO = cardkeyProductService.getCardKeyProductByNo(productNo);
                final boolean insert = productDO == null;
                if (insert) {
                    productDO = BeanUtils.toBean(product, CardkeyProductDO.class);
                    cardkeyProductService.createCardkeyProduct(productDO);
                } else {
                    // TODO: 待完善
//                    cardkeyProductService.updateCardkeyProduct(productDO);
                }
            }
        }

        log.info("获取卡密商品-完成");
    }

    // 测试环境, 将所有卡密商品都生成一遍卡密, 看看哪些可用, 将不可用的删除掉
    private void checkGenCardInTestEnv() {
        final List<CardkeyProductDO> productList = cardkeyProductService.selectBySource(CardKeySourceEnum.BLACK_HAWK_API);

        final ExecutorService generateSecretExecutor = ThreadUtil.newFixedExecutor(8, "checkGenCardInTestEnv", true);
        final CountDownLatch latch = new CountDownLatch(productList.size());
        for (CardkeyProductDO cardKeyProduct : productList) {
            generateSecretExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        final String productNo = cardKeyProduct.getProductNo();
                        final Integer denomination = cardKeyProduct.getDenominationMin();
                        final String orderNo = IdUtil.fastSimpleUUID();
                        final GenerateEGiftRequest request = createGenerateEGiftRequestDTO(orderNo, denomination, cardKeyProduct);
                        final String currencyCode = cardKeyProduct.getCurrencyCode();
                        final GenerateEGiftRequestWrapper requestWrapper = new GenerateEGiftRequestWrapper(
                                request, IdUtil.fastSimpleUUID(), productNo, denomination, currencyCode);

                        final GenerateEGiftResponseWrapper generateCardResult = cardKeyThirdPartyApi.generateCard(requestWrapper);
                        if (generateCardResult.isGenerateEGiftSuccess()
                                && generateCardResult.getGetAccountResponseWrapper() != null
                                && generateCardResult.getGetAccountResponseWrapper().isSuccess()) {
                            log.info("领取卡密-调用api生成密钥-单个成功, productNo = {}, denomination = {}, generateCardResult = {}",
                                    productNo, denomination, generateCardResult);
                        } else {
                            log.error("领取卡密-调用api生成密钥-单个失败, productNo = {}, denomination = {}, generateCardResult = {}",
                                    productNo, denomination, generateCardResult);
                            // 将商品设置为不可用
                            cardkeyProductService.deleteCardkeyProduct(cardKeyProduct.getId());
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            });
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    public static void main(String[] args) {
        String str = "https://api.certification.blackhawknetwork.com/productManagement/v1/product/KLMJ97GDMQRBNT60KB0Z0ANR6R";
        System.out.println(str.substring(str.lastIndexOf("/") + 1));


    }

}
